package com.gzx.bi.modular.repayment.client.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.gzx.bi.modular.repayment.client.bo.GzxRepaymentClientAddBo;
import com.gzx.bi.modular.repayment.client.bo.GzxRepaymentClientImportBo;
import com.gzx.bi.modular.repayment.client.bo.GzxRepaymentClientQueryBo;
import com.gzx.bi.modular.repayment.client.bo.excel.Gzx360ExcelBo;
import com.gzx.bi.modular.repayment.client.bo.excel.GzxJDExcelBo;
import com.gzx.bi.modular.repayment.client.bo.excel.GzxRepaymentClientCommonExcelBo;
import com.gzx.bi.modular.repayment.client.bo.excel.GzxSNXJExcelBo;
import com.gzx.bi.modular.repayment.client.consts.GzxCompanyConstant;
import com.gzx.bi.modular.repayment.client.entity.GzxRepaymentClient;
import com.gzx.bi.modular.repayment.client.mapper.GzxRepaymentClientMapper;
import com.gzx.bi.modular.repayment.client.service.GzxRepaymentClientBatchInsertService;
import com.gzx.bi.modular.repayment.client.service.GzxRepaymentClientService;
import com.gzx.bi.modular.repayment.client.vo.GzxRepaymentClientVo;
import com.gzx.tjzy.feign.GzxProjectFeign;
import com.gzx.tjzy.feign.resp.project.GzxProjectRespDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import vip.xiaonuo.auth.core.util.StpLoginUserUtil;
import vip.xiaonuo.common.consts.IntConstant;
import vip.xiaonuo.common.exception.CommonException;
import vip.xiaonuo.common.mvc.service.impl.BaseServiceImpl;
import vip.xiaonuo.common.tenant.TenantContext;
import vip.xiaonuo.common.util.CommonDateUtil;
import vip.xiaonuo.common.util.CommonExcelUtil;
import vip.xiaonuo.common.util.MapstructUtils;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author zjt
 * @description
 * @date 2025/4/1 16:25
 */
@Slf4j
@Service
@DS("bi")
public class GzxRepaymentClientServiceImpl extends BaseServiceImpl<GzxRepaymentClientAddBo, GzxRepaymentClientVo, GzxRepaymentClientQueryBo, GzxRepaymentClientMapper, GzxRepaymentClient> implements GzxRepaymentClientService {

    public GzxRepaymentClientServiceImpl(GzxRepaymentClientMapper gzxRepaymentClientMapper, GzxRepaymentClientBatchInsertService repaymentClientBatchInsertService, GzxProjectFeign projectFeign) {
        super(gzxRepaymentClientMapper);
        this.repaymentClientBatchInsertService = repaymentClientBatchInsertService;
        this.projectFeign = projectFeign;
    }

    private final GzxRepaymentClientBatchInsertService repaymentClientBatchInsertService;

    private final GzxProjectFeign projectFeign;

    private static final String ERROR_MSG = "甲方还款数据导入出现异常";

    /**
     * 插入一个新的业务对象。
     *
     * @param gzxRepaymentClientAddBo 业务对象
     */
    @Override
    public void insert(GzxRepaymentClientAddBo gzxRepaymentClientAddBo) {
        GzxRepaymentClient repaymentClient = MapstructUtils.convert(gzxRepaymentClientAddBo, GzxRepaymentClient.class);
        repaymentClient.setAllJson(JSON.toJSONString(repaymentClient));
        baseMapper.insert(repaymentClient);
    }

    /**
     * 导入Excel
     *
     * @param repaymentClientImportBo
     * @param multipartFile
     */
    @Override
    public void importExcel(GzxRepaymentClientImportBo repaymentClientImportBo, MultipartFile multipartFile) {
        List<GzxRepaymentClient> repaymentClientList = analysisExcel(repaymentClientImportBo, multipartFile);
        if (CollectionUtil.isNotEmpty(repaymentClientList)) {
            List<List<GzxRepaymentClient>> batchList = Lists.partition(repaymentClientList, IntConstant.VALUE_10000);
            long startTime = System.currentTimeMillis();
            // 使用固定大小为10的虚拟线程池
            try (var executor = Executors.newFixedThreadPool(10,
                    Thread.ofVirtual().name("virtual-thread-", 0).factory())) {
                List<CompletableFuture<Void>> futures = batchList.stream()
                        .map(batch -> CompletableFuture.runAsync(() -> {
                            try {
                                TenantContext.disableTenantFilter();
                                repaymentClientBatchInsertService.insertBatch(batch);
                            } catch (Exception e) {
                                log.error(ERROR_MSG, e);
                                throw new CommonException(ERROR_MSG);
                            } finally {
                                TenantContext.enableTenantFilter();
                                TenantContext.clear();
                            }
                        }, executor))
                        .toList();
                // 等待所有任务完成
                CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
            } catch (Exception e) {
                log.error(ERROR_MSG, e);
                throw new CommonException(ERROR_MSG);
            }
            log.info("处理完成，共{}条数据，总耗时: {}", repaymentClientList.size(), System.currentTimeMillis() - startTime);
        }
    }

    /**
     * 查询大于ID的数据
     *
     * @param queryWrapper
     * @return
     */
    @Override
    public List<GzxRepaymentClient> selectList(LambdaQueryWrapper<GzxRepaymentClient> queryWrapper) {
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    protected LambdaQueryWrapper<GzxRepaymentClient> buildQueryWrapper(GzxRepaymentClientQueryBo queryBo) {
        return Wrappers.<GzxRepaymentClient>lambdaQuery()
                .eq(queryBo.getDeptId() != null, GzxRepaymentClient::getDeptId, queryBo.getDeptId())
                .eq(StringUtils.isNotBlank(queryBo.getProjectCode()), GzxRepaymentClient::getProjectCode, queryBo.getProjectCode())
                .between(StringUtils.isNotBlank(queryBo.getBeginTime()) && StringUtils.isNotBlank(queryBo.getEndTime()), GzxRepaymentClient::getRepaymentDate, queryBo.getBeginTime(), queryBo.getEndTime())
                .orderByDesc(GzxRepaymentClient::getId);
    }

    private List<GzxRepaymentClient> analysisExcel(GzxRepaymentClientImportBo repaymentClientImportBo, MultipartFile multipartFile) {
        String companyCode = repaymentClientImportBo.getCompanyCode();
        Long deptId = repaymentClientImportBo.getDeptId();
        try {
            // 根据项目代码选择不同的处理方式
            return switch (companyCode) {
                case GzxCompanyConstant.JDJR -> processExcelData(multipartFile, GzxJDExcelBo.class, null, deptId);
                case GzxCompanyConstant.SNXJ -> processExcelData(multipartFile, GzxSNXJExcelBo.class, null, deptId);
                case GzxCompanyConstant.JR360 -> processExcelData(multipartFile, Gzx360ExcelBo.class, null, deptId);
                case null, default -> {
                    // 2. 获取该公司下的项目列表
                    List<GzxProjectRespDto> projectRespDtoList = projectFeign.getProjectByCompany(companyCode);
                    if (CollectionUtil.isEmpty(projectRespDtoList)) {
                        throw new CommonException("未找到该公司对应的项目");
                    }
                    Map<String, String> projectMap = projectRespDtoList.stream().collect(Collectors.toMap(GzxProjectRespDto::getProjectName, GzxProjectRespDto::getProjectCode));
                    // 3. 使用通用模板处理
                    yield processExcelData(
                            multipartFile,
                            GzxRepaymentClientCommonExcelBo.class,
                            projectMap,
                            deptId);
                }
            };
        } catch (
                Exception e) {
            log.error("Excel解析失败: companyCode={}", companyCode, e);
            throw new CommonException("Excel解析失败: " + e.getMessage());
        }
    }

    /**
     * 通用Excel数据处理方法
     *
     * @param <T> Excel数据对象类型
     */
    private <T> List<GzxRepaymentClient> processExcelData(MultipartFile multipartFile,
                                                          Class<T> excelBoClass,
                                                          Map<String, String> projectMap,
                                                          Long deptId) {
        List<T> excelBoList = CommonExcelUtil.importExcel(multipartFile, excelBoClass);
        String tenantId = StpLoginUserUtil.getLoginUser().getTenantId();
        String userId = StpLoginUserUtil.getLoginUser().getId();
        // 使用并行流处理大数据量导入
        return excelBoList.parallelStream()
                .map(excelBo -> convertToRepaymentClient(excelBo, projectMap, deptId, tenantId, userId))
                .toList();
    }

    /**
     * 将Excel数据对象转换为GzxRepaymentClient
     */
    private <T> GzxRepaymentClient convertToRepaymentClient(T excelBo,
                                                            Map<String, String> projectMap,
                                                            Long deptId,
                                                            String tenantId,
                                                            String userId) {
        GzxRepaymentClient repaymentClient = new GzxRepaymentClient();
        BeanUtils.copyProperties(excelBo, repaymentClient);
        if (excelBo instanceof GzxJDExcelBo jdExcelBo) {
            handleJDBatch(jdExcelBo, repaymentClient);
        } else if (excelBo instanceof GzxSNXJExcelBo snxjExcelBo) {
            handleSNXJBatch(snxjExcelBo, repaymentClient);
        } else if (excelBo instanceof Gzx360ExcelBo gzx360ExcelBo) {
            handle360Batch(gzx360ExcelBo, repaymentClient);
        } else {
            handleCommonBatch(repaymentClient, projectMap);
        }
        repaymentClient.setDeptId(deptId);
        repaymentClient.setRepaymentDate(CommonDateUtil.convertToDate(repaymentClient.getRepaymentDate()));
        repaymentClient.setOverdueDate(CommonDateUtil.convertToDate(repaymentClient.getOverdueDate()));
        repaymentClient.setTenantId(tenantId);
        repaymentClient.setCreateUser(userId);
        repaymentClient.setAllJson(JSON.toJSONString(excelBo));
        return repaymentClient;
    }

    private void handleJDBatch(GzxJDExcelBo jdExcelBo, GzxRepaymentClient repaymentClient) {
        String caseStartDate = jdExcelBo.getCaseStartDate();
        if (caseStartDate == null) {
            throw new CommonException("caseStartDate 不能为空");
        }
        String currentMonth = CommonDateUtil.getCurrentMonthFirstDay().replace("-", "").substring(0, 6);
        repaymentClient.setDebtorUserNo(jdExcelBo.getDebtorUserNo());
        repaymentClient.setBatch(caseStartDate.substring(0, 6).equals(currentMonth) ? "T0" : "T1");
    }

    private void handleSNXJBatch(GzxSNXJExcelBo snxjExcelBo, GzxRepaymentClient repaymentClient) {
        // 1. 设置欠款人用户编号
        String debtorUserNo = snxjExcelBo.getProductName()
                + Optional.ofNullable(snxjExcelBo.getDebtorCardId())
                .orElse("")
                .replace("*", "");
        repaymentClient.setDebtorUserNo(debtorUserNo);
        // 2. 确定批次类型
        String batchName = Optional.ofNullable(snxjExcelBo.getBatchName())
                .orElse("");
        String batch = determineSNXJBatchType(batchName);
        repaymentClient.setBatch(batch);
        repaymentClient.setProjectCode("SNXJ");
    }

    /**
     * 根据批次名称确定批次类型
     *
     * @param batchName 批次名称
     * @return 确定的批次类型
     */
    private String determineSNXJBatchType(String batchName) {
        if (batchName.contains("M12")) {
            return "M12+";
        } else if (batchName.contains("M1")) {
            return "M1";
        } else if (batchName.contains("M2")) {
            return "M2";
        } else if (batchName.contains("M3")) {
            return "M3";
        }
        return "苏宁法诉";
    }

    private void handle360Batch(Gzx360ExcelBo gzx360ExcelBo, GzxRepaymentClient repaymentClient) {
        repaymentClient.setBatch(CommonDateUtil.isBeforeLastMonth(gzx360ExcelBo.getBatch()) ? CommonDateUtil.getLastMonth() : gzx360ExcelBo.getBatch().substring(0, gzx360ExcelBo.getBatch().length() - 2));
        repaymentClient.setProjectCode(determine360Project(gzx360ExcelBo.getQueue(), gzx360ExcelBo.getStageMappingRange()));
    }

    private String determine360Project(String queue, String stageMappingRange) {
        if (queue.endsWith("100") || queue.endsWith("104")) {
            return "360JR-" + stageMappingRange;
        }
        if (queue.endsWith("102")) {
            return "360JR-102";
        }
        if (queue.endsWith("103")) {
            return "360JR-103";
        }
        return null;
    }

    private void handleCommonBatch(GzxRepaymentClient repaymentClient, Map<String, String> projectMap) {
        if (projectMap.containsKey(repaymentClient.getProjectCode())) {
            repaymentClient.setProjectCode(projectMap.get(repaymentClient.getProjectCode()));
        }
    }
}
